﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace X3dTest
{
    public class Fire : X3dInterfaceObject
    {

        private string Color;
        /// <summary>
        /// 基本颜色
        /// </summary>
        public string color
        {
            get { return Color; }
            set { Color = value; }
        }
        private string Num;
        /// <summary>
        /// 大小
        /// </summary>
        public string num
        {
            get { return Num; }
            set { Num = value; }
        }
        private string Range;
        /// <summary>
        /// 范围
        /// </summary>
        public string range
        {
            get { return Range; }
            set { Range = value; }
        }
        private string Life;
        /// <summary>
        /// 生命
        /// </summary>
        public string life
        {
            get { return Life; }
            set { Life = value; }
        }
        private string Gravity;
        /// <summary>
        /// 重力加速
        /// </summary>
        public string gravity
        {
            get { return Gravity; }
            set { Gravity = value; }
        }
        private string Wind;
        /// <summary>
        /// 风速
        /// </summary>
        public string wind
        {
            get { return Wind; }
            set { Wind = value; }
        }
        
        /// <summary>
        /// 定义环境节点 重力加速度，风力等
        /// </summary>
        public object defEnvironment()
        {
            ProtoDeclare pd = new ProtoDeclare();
            pd.name = "Environment";
            ProtoInterface pi = new ProtoInterface();
            pd.protoInterface = pi;
            //主体颜色
            pi.addChildren(new field("color", "SFColor", "inputOutput"));
            //范围
            pi.addChildren(new field("range", "SFVec3f", "inputOutput"));
            //粒子生命
            pi.addChildren(new field("life", "SFInt32", "inputOutput"));
            //各个方向的速度
            pi.addChildren(new field("gravity", "SFVec3f", "inputOutput"));
            //各个方向的风速
            pi.addChildren(new field("wind", "SFVec3f", "inputOutput"));
            Group g = new Group() { DEF = "declare_env" };
            pd.protoBody.addChildren(new Transform() { DEF = "empty" });
            g.addChildren(pd);
            return g;
        }
        /// <summary>
        /// 定义粒子
        /// </summary>
        /// <returns></returns>
        public object defParticle()
        {
            //ProtoInterface
            ProtoInterface pi = new ProtoInterface();
            /*粒子的各种属性可以通过如下接口输入，从而实现粒子的静态属性的设置*/
            //Material 属性  
            pi.addChildren(new field("position", "SFVec3f", "inputOutput"));
            pi.addChildren(new field("ambientIntensity", "SFFloat", "inputOutput"));
            pi.addChildren(new field("diffuseColor", "SFColor", "inputOutput"));
            pi.addChildren(new field("emissiveColor", "SFColor", "inputOutput"));
            pi.addChildren(new field("shininess", "SFFloat", "inputOutput"));
            pi.addChildren(new field("specularColor", "SFColor", "inputOutput"));
            pi.addChildren(new field("transparency", "SFFloat", "inputOutput"));
            //附加属性
            pi.addChildren(new field("velocity", "SFVec3f", "inputOutput"));//速度
            pi.addChildren(new field("age", "SFInt32", "inputOutput"));//年龄
            //ProtoBody add group
            ProtoBody pb = new ProtoBody();
            Group g = new Group();
            pb.addChildren(g);

            Transform t = new Transform();
            g.addChildren(t);

            t.DEF = "t1";
            //transform add <IS/><Shape/>
            IS is_tp = new IS();
            is_tp.addChildren(new connect("translation", "position"));
            t.addChildren(is_tp);

            Shape shape = new Shape();
            Material m = new Material();
            IS is_mdc = new IS();
            is_mdc.addChildren(new connect("ambientIntensity", "ambientIntensity"));
            is_mdc.addChildren(new connect("diffuseColor", "diffuseColor"));
            is_mdc.addChildren(new connect("emissiveColor", "emissiveColor"));
            is_mdc.addChildren(new connect("shininess", "shininess"));
            is_mdc.addChildren(new connect("specularColor", "specularColor"));
            is_mdc.addChildren(new connect("transparency", "transparency"));
            m.addChildren(is_mdc);
            Sphere sphere = new Sphere("0.025");
            Appearance app = new Appearance(m);
            shape.appearance = app;
            shape.gemeotry = sphere;
            t.addChildren(shape);

            Group pd_g = new Group();
            pd_g.DEF = "declare_particle";//包裹整个节点定义
            ProtoDeclare pd = new ProtoDeclare(pb, pi);//节点定义
            pd.name = "FireNode";
            pd_g.addChildren(pd);
            return pd_g;
        }
        /// <summary>
        /// 创建环境
        /// </summary>
        /// <param name="name">节点名称</param>
        /// <param name="color">主体颜色</param>
        /// <param name="range">活动范围</param>
        /// <param name="gravity">重力加速度</param>
        /// <param name="wind">风速</param>
        /// <returns>环境对象 （包括：自身限制和外界限制）</returns>
        public object createEnvironment(string name, string color, string range,string life, string gravity, string wind)
        {
            return new Environment.ProtoInstance(name ,color , range, life, gravity ,wind);
        }
        /// <summary>
        /// 创建粒子
        /// </summary>
        /// <param Name="num">个数</param>
        /// <returns></returns>
        public object createParticles(int num)
        {
            string[] strc = color.Split(' ');
            string[] strr = range.Split(' ');
            float[] c = new float[3];
            float[] p = new float[3];
            for (int i = 0; i < 3; i++)
            {
                c[i] = float.Parse(strc[i]);
                p[i] = Math.Abs(float.Parse(strr[i]));
            }
            Group g = new Group();
            g.DEF = "fire_group";
            //环境节点为第一个[0]
            g.addChildren(createEnvironment("Environment", this.color, this.range, this .life , this .gravity , this .wind ));
            
            for (int i = 0; i < num; i++)
            {
                //g.addChildren(new ProtoInstance("FireNode", randValue(c), randValue(p)));
                g.addChildren(new ProtoInstance("FireNode") {});
            }
            return g;
        }
        /// <summary>
        /// 设置属性表
        /// </summary>
        /// <param Name="color">颜色</param>
        /// <param Name="size">大小</param>
        /// <param Name="range">范围</param>
        public void setAttributes(string color, string size, string range,string life,string gravity,string wind)
        {
            this.color = color;
            this.num = size;
            this.range = range;
            this.life = life;
            this.gravity = gravity;
            this.wind = wind;
            createFire();
        }

        private Random rand = new Random();
        public string randValue(float[] f)
        {
            string s = "";
            for (int i = 0; i < f.Length; i++)
            {
                string temp = (rand.NextDouble() * f[i]).ToString();
                if (temp.Length >= 5) temp = temp.Remove(5);
                s += temp + " ";
            }
            return s;
        }
        /// <summary>
        /// 根据属性创建"火"
        /// </summary>
        public void createFire()
        {
            Group group = new Group();
            group.DEF = "fire";
            group.addChildren(defEnvironment());
            group.addChildren(defParticle());
            group.addChildren(createParticles(Int32.Parse (this.num) ));
            group.addChildren(createScript());
            //timer
            TimeSensor time = new TimeSensor("clock", "1", "true");
            ROUTE route = new ROUTE("fraction_changed", time.DEF, "start", "control");
            group.addChildren(time);
            group.addChildren(route);
            this.addChildren(group);
        }

        public object createExternProto(string nodeFilePath, string nodeName)
        {
            //extern 
            ExternProtoDeclare ex_proto = new ExternProtoDeclare();
            ex_proto.url = nodeFilePath + "#" + nodeName;
            ex_proto.name = nodeName;
            ex_proto.addChildren(new field("position", "SFVec3f", "inputOutput"));
            ex_proto.addChildren(new field("diffuseColor", "SFColor", "inputOutput"));
            return ex_proto;
        }
        /// <summary>
        /// 创建动画控制脚本
        /// </summary>
        /// <returns></returns>
        public object createScript()
        {
            //Script add <Name/><![CDATA[javascript:]]>
            Script s = new Script();
            s.DEF = "control";
            s.directOutput = "true";
            //s.url = "function.js";

            field f_g = new field("group", "SFNode", "inputOutput");
            f_g.addChildren(new Group("fire_group"));
            s.addChildren(f_g);
            //全部节点
            s.addChildren(new field("nodes", "MFNode", "inputOutput"));
            //主体颜色
            s.addChildren(new field("color", "SFColor", "inputOutput"));
            //活动范围
            s.addChildren(new field("range", "SFVec3f", "inputOutput"));
            //生命长度 60
            s.addChildren(new field("life", "SFInt32", "inputOutput"));
            //重力加速度
            s.addChildren(new field("gravity", "SFVec3f", "inputOutput"));
            //风速
            s.addChildren(new field("wind", "SFVec3f", "inputOutput"));
            //启动函数
            s.addChildren(new field("start", "SFFloat", "inputOnly"));
            //
            string context = "<![CDATA[\njavascript:\n"
                +"function initialize()\n"
                +"{\n"
                +"nodes=group.children;\n"
                +"color=nodes[0].color;\n"
                +"range=nodes[0].range;\n"
                +"life=nodes[0].life;\n"
                +"gravity=nodes[0].gravity;\n"
                +"wind=nodes[0].wind;\n"
                +"for(i=1;i<nodes.length;i++)\n"
                +"{\n"
                +"init_particle(i);\n"
                +"}\n"
                +"}\n"
                +"\n"
                +"function init_particle(i)\n"
                +"{\n"
                +"nodes[i].age=0;\n"
                +"\n"

                +"nodes[i].velocity[0]=Math.random()*0.01-0.005;\n"
                +"nodes[i].velocity[1]=Math.random()*0.03;\n"
                +"nodes[i].velocity[2]=Math.random()*0.01-0.005;\n"
                +"\n"
                +"\n"

                +"nodes[i].diffuseColor[0]=1;\n"
                +"nodes[i].diffuseColor[1]=1;\n"
                +"nodes[i].diffuseColor[2]=Math.random();\n"
                +"\n"

                +"nodes[i].emissiveColor[0]=1;\n"
                +"nodes[i].emissiveColor[1]=1;\n"
                +"nodes[i].emissiveColor[2]=Math.random();\n"
                +"\n"
                +"nodes[i].ambientIntensity=1;\n"
                +"\n"

                +"nodes[i].position[0]=(Math.random()-0.5)*range[0];\n"
                +"nodes[i].position[1]=Math.random()*0.1;\n"
                +"nodes[i].position[2]=(Math.random()-0.5)*range[2];\n"
                +"\n"
 
                +"nodes[i].transparency=0.5;\n"
                +"}\n"
                +"function update()\n"
                +"{\n"
                +"for(i=1;i<nodes.length;i++)\n"
                +"{\n"
                +"if(Math.random()>0.4)\n"
                +"{\n"
                +"nodes[i].age+=1;\n"
                +"if(nodes[i].age>30||nodes[i].diffuseColor[0]<0.001)\n"
                +"{\n"
                +"wind[0]=Math.random()*0.05-0.025;\n"
                +"wind[1]=Math.random()*0.05-0.025;\n"
                +"wind[2]=Math.random()*0.05-0.025;\n"
                +"init_particle(i);\n"
                +"}\n"
                +"nodes[i].diffuseColor[1]-=Math.random()*0.08;\n"
                +"nodes[i].emissiveColor[1]-=Math.random()*0.08;\n"
                + "nodes[i].position[0]+=nodes[i].velocity[0]-wind[0];\n"
                + "nodes[i].position[1]+=nodes[i].velocity[1]-wind[1];\n"
                + "nodes[i].position[2]+=nodes[i].velocity[2]-wind[2];\n"
                +"}\n"
                +"}\n"
                +"}\n"
                
                +"function setPosition(dest,src)\n"
                +"{\n"
                +"dest[0]=src[0]*(Math.random()-0.5);\n"
                +"temp=(dest[0]*src[0])/src[2];\n"
                +"//dest[2]=src[2]*Math.random()*2-1;\n"
                +"dest[2]=Math.sqrt( (src[0]+temp)*(src[0]-temp) )*(Math.random()>0.5?1:-1) * Math.random();\n"
                +"dest[1]=Math.exp(-1*(Math.pow(dest[0],2)+Math.pow(dest[2],2)))*Math.random();\n"
                +"}\n"
                +"function setColor(dest,src)\n"
                +"{\n"
                +"for(i=0;i<3;i++)\n"
                +"{\n"
                +"dest[i]=src[i]*Math.random();\n"
                +"}\n"
                +"}\n"
                +"\n"
                +"function start(value)\n"
                +"{\n"
                +"update();\n"
                +"// Browser.setDescription(nodes[1].age);\n"
                +"}\n"

                + "\n]]>\n";
            s.addChildren(context);
            return s;
        }

        public override  string getAttrs()
        {
            return "color|" + this.color + "|max:1 min:0,"
                    + "num|" + this.num + "|int32,"
                    + "range|" + this.range + "|float,"
                    + "life|" + this.life + "|int,"
                    + "gravity|" + this.gravity + "|float[3],"
                    + "wind|" + this.wind + "|float[3]";
        }

        public override  void setAttrs(string attrs)
        {
            string[][] temp = X3DString.getArrayByAttrs(attrs);
            for (int i = 0; i < temp.Length; i++)
            {
                switch (temp[i][0])
                {
                    case "color":
                        this.color = temp[i][1];
                        break;
                    case "num":
                        this.num = temp[i][1];
                        break;
                    case "range":
                        this.range = temp[i][1];
                        break;
                    case "life":
                        this.life = temp[i][1];
                        break;
                    case "gravity":
                        this.gravity = temp[i][1];
                        break;
                    case "wind":
                        this.wind = temp[i][1];
                        break;

                }
            }
            setAttributes(this.color, this.num, this.range,this .life ,this .gravity ,this .wind );
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        public Fire()
        {
            this.life = "100";
            this.gravity = "0.0 9.8 0.0";
            this.wind = "0.6 0.1 0.2";
            setAttributes("1 0 0", "600", "1 1 1",this .life ,this .gravity ,this .wind );
        }
        public Fire(string color, string size, string range)
        {
            this.life = "100";
            this.gravity = "0.0 9.8 0.0";
            this.wind = "0.6 0.1 0.2";
            setAttributes(color, size, range, this.life, this.gravity, this.wind);
        }
    }
}
